package com.jake.xfeed.recyclerfeed.pager;

import android.content.Context;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.PagerSnapHelper;
import androidx.recyclerview.widget.RecyclerView;

import com.jake.xfeed.recyclerfeed.TouchEventDeterminer;


public class RecyclerViewPageFeed extends RecyclerView implements TouchEventDeterminer.IChild, TouchEventDeterminer.IParent {
    private final int mOrientation;
    private LinearLayoutManager mLayoutManager;
    private PagerSnapHelper mPagerSnapHelper;
    private TouchEventDeterminer.ParentHelper mTouchEventDeterminerParentHelper;
    @NonNull
    private final PageChangedHelper mPageChangedHelper;
    private Runnable mCallbackPageSelectedTask;

    public RecyclerViewPageFeed(@NonNull Context context, @Orientation int orientation) {
        super(context);
        mOrientation = orientation;
        mLayoutManager = new PageSnapLimitLinearLayoutManager(this, orientation, false);
        this.setLayoutManager(mLayoutManager);
        this.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);
        mPageChangedHelper = new PageChangedHelper(mLayoutManager);
        addOnScrollListener(mPageChangedHelper);
        mPagerSnapHelper = new PagerSnapHelper();
        mPagerSnapHelper.attachToRecyclerView(this);
        mTouchEventDeterminerParentHelper = TouchEventDeterminer.ontainParentHelper();
        mTouchEventDeterminerParentHelper.setContinueTouchWhenChildStop(true);
        TouchEventDeterminer.childAttachToParent(this);
        mCallbackPageSelectedTask = new Runnable() {
            @Override
            public void run() {
                mPageChangedHelper.customPageSelected(mLayoutManager.findFirstVisibleItemPosition());
            }
        };
    }

    public View getPageView(int position) {
        return mLayoutManager.findViewByPosition(position);
    }

    @Override
    public void scrollToPosition(int position) {
        super.scrollToPosition(position);
        if (getChildCount() > 0) {
            post(new Runnable() {
                @Override
                public void run() {
                    mPageChangedHelper.customPageSelected(position);
                }
            });

        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (getChildCount() > 0) {
            if (mCallbackPageSelectedTask != null) {
                post(mCallbackPageSelectedTask);
                mCallbackPageSelectedTask = null;
            }
        }
    }

    public int getCurrentPosition() {
        return mLayoutManager.findFirstVisibleItemPosition();
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return mTouchEventDeterminerParentHelper.dispatchTouchEvent(this, ev, new TouchEventDeterminer.SuperDispatchTouchEventListener() {
            @Override
            public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
                return superDispatchTouchEvent(ev);
            }
        });
    }

    public boolean superDispatchTouchEvent(@NonNull MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        if (mTouchEventDeterminerParentHelper.isTouchEventLocked()) {
            return false;
        }
        return super.onInterceptTouchEvent(e);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        if (mTouchEventDeterminerParentHelper.isTouchEventLocked()) {
            return false;
        }
        return super.onTouchEvent(e);
    }

    @Override
    public void setAdapter(@Nullable Adapter adapter) {
        Adapter lastAdapter = getAdapter();
        if (lastAdapter instanceof RecyclerViewPagerAdapter) {
            removeOnPageChangedListener(((RecyclerViewPagerAdapter<?>) lastAdapter).getOnPageChangedListener());
        }
        super.setAdapter(adapter);
        if (adapter instanceof RecyclerViewPagerAdapter) {
            addOnPageChangedListener(((RecyclerViewPagerAdapter<?>) adapter).getOnPageChangedListener());
        }
    }


    @Override
    public void addChildDeterminer(TouchEventDeterminer.IChild child) {
        mTouchEventDeterminerParentHelper.addChildDeterminer(child);
    }

    @Override
    public void removeChildDeterminer(TouchEventDeterminer.IChild child) {
        mTouchEventDeterminerParentHelper.removeChildDeterminer(child);
    }

    public void addOnPageChangedListener(OnPageChangedListener listener) {
        mPageChangedHelper.addOnPageChangedListener(listener);
    }

    public void removeOnPageChangedListener(OnPageChangedListener listener) {
        mPageChangedHelper.removeOnPageChangedListener(listener);
    }


    @Override
    public boolean determineTouchEvent(MotionEvent ev) {
        return true;
    }


    public interface OnPageChangedListener {
        void onPageScrolled(int lastPosition, int position, float positionOffset,
                            @Px int positionOffsetPixels);

        void onPageSelected(int lastPosition, int position);

        void onPageScrollStateChanged(int state);
    }

    public static abstract class OnPageChangedListenerAdapter implements OnPageChangedListener {
        @Override
        public void onPageScrolled(int lastPosition, int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int lastPosition, int position) {

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    }

    private static class PageSnapLimitLinearLayoutManager extends LinearLayoutManager {
        private RecyclerView mRecyclerView;
        private boolean mInterceptDrag = false;
        private int mInterceptDragScroll = 0;
        private boolean mHasCallSuperScroll = false;

        public PageSnapLimitLinearLayoutManager(@NonNull RecyclerView recyclerView, @Orientation int orientation, boolean reverseLayout) {
            super(recyclerView.getContext(), orientation, reverseLayout);
            mRecyclerView = recyclerView;
            mRecyclerView.addOnScrollListener(mOnScrollListener);
        }

        @Override
        public void onLayoutCompleted(State state) {
            super.onLayoutCompleted(state);
        }

        private OnScrollListener mOnScrollListener = new OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
                    mInterceptDrag = true;
                    mInterceptDragScroll = 0;
                } else if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    mInterceptDrag = false;
                }
            }
        };


        @Override
        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
            if (mInterceptDrag) {
                mInterceptDragScroll += dx;
                int width = mRecyclerView.getWidth();
                if (Math.abs(mInterceptDragScroll) >= width) {
                    int consumedX = Math.abs(mInterceptDragScroll) - width;
                    if (dx < 0) {
                        consumedX = -consumedX;
                    }
                    dx = dx - consumedX;
                    int ret = 0;
                    if (dx != 0 && !mHasCallSuperScroll) {
                        mHasCallSuperScroll = true;
                        ret = super.scrollHorizontallyBy(dx, recycler, state);
                    }
                    return ret;
                }
                mHasCallSuperScroll = false;
            }
            return super.scrollHorizontallyBy(dx, recycler, state);
        }


        @Override
        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
            if (mInterceptDrag) {
                mInterceptDragScroll += dy;
                int height = mRecyclerView.getHeight();
                if (Math.abs(mInterceptDragScroll) >= height) {
                    int consumedY = Math.abs(mInterceptDragScroll) - height;
                    if (dy < 0) {
                        consumedY = -consumedY;
                    }
                    dy = dy - consumedY;
                    int ret = 0;
                    if (dy != 0 && !mHasCallSuperScroll) {
                        mHasCallSuperScroll = true;
                        ret = super.scrollVerticallyBy(dy, recycler, state);
                    }
                    return ret;
                }
                mHasCallSuperScroll = false;
            }
            return super.scrollVerticallyBy(dy, recycler, state);
        }
    }

    @NonNull
    @Override
    public View getView() {
        return this;
    }
}
